﻿#include "PCRemoteCtrl.h"

#include <QtWidgets/QLayout>
#include <QtGui/QKeyEvent>
#include <QtGui/QScreen>
#include <QtGui/QGuiApplication>
#include <sstream>
#include <thread>


#include "uWebSockets/QtuHttpWebSocket.h"
#include "libqr/QrEncode.h"

#include "SysCtrl/SysCtrl.h"




PCRemoteCtrl::PCRemoteCtrl(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);

#if 1
	this->setWindowFlags(this->windowFlags() & ~(Qt::WindowContextHelpButtonHint | Qt::WindowMaximizeButtonHint));

	if (0)// this->layout())
	{
		this->layout()->setSizeConstraint(QLayout::SetFixedSize);
	}
	else
	{
		setFixedSize(this->width(), this->height());
	}
#endif

	ReadJsonConfig();

	InitTray();

	InitWebsocket();

#ifdef NDEBUG
	QTimer::singleShot(100, [&]() {
		this->hide();	// 隐藏
	});
#endif
}

void PCRemoteCtrl::SlotStatusBarMessage(QString strMessage, StatusBarMsgColor iLevel, int iTimeoutMs)
{
	qDebug() << strMessage;
	QString strSheet;
	switch (iLevel)
	{
	case StatusBarMsgColor_Black:
		strSheet = "QStatusBar{color: black;font-size: 14px;}";
		break;
	case StatusBarMsgColor_Green:
		strSheet = "QStatusBar{color: green;font-size: 14px;}";
		break;
	case StatusBarMsgColor_Blue:
		strSheet = "QStatusBar{color: blue;font-size: 14px;}";
		break;
	case StatusBarMsgColor_Red:
		strSheet = "QStatusBar{color: red;font-size: 14px;}";
		QApplication::beep();
		break;
	default:
		strSheet = "QStatusBar{color: black;font-size: 14px;}";
		break;
	}
	statusBar()->setStyleSheet(strSheet);
	ui.statusBar->showMessage(strMessage, iTimeoutMs);
	WebsocketProcess::Instance()->SendStatusBarMsg(strMessage, iLevel, iTimeoutMs);
}

void PCRemoteCtrl::SlotWebMsg(QString strMsgQt)
{
	// 最后一个模态消息
	static std::string mstrLastModalMsg;
	std::string strMsgUtf8 = strMsgQt.toUtf8().toStdString();
	try
	{
		Json::Reader reader;
		Json::Value root;
		if (!reader.parse(strMsgUtf8, root))
		{// json解析
			qDebug() << "json parse failed. json:" << strMsgQt;
			this->SlotStatusBarMessage(tr("Web消息解析失败1。"));
			return;
		}

		QString strType = GetJsonStrVal(root, "msgType");
		//qDebug() << "收到页面消息：" << strType;
		//QMessageBox::information(this, tr("提示"), tr("收到页面消息:%1").arg(strType));
		//SlotStatusBarMessage(tr("收到页面消息:%1").arg(strType), StatusBarMsgColor_Black);
		if ("hello" == strType)
		{// 已经连接上，需要初始化数据

			{// 新连接
				std::stringstream strSteam;
				strSteam << "{\"msgType\":\"" << "hello" << "\"" << "}";
				WebsocketProcess::Instance()->SendToAllClient(strSteam.str());
			}

			{// 最后一个弹窗消息
				if (!mstrLastModalMsg.empty())
				{
					WebsocketProcess::Instance()->SendToAllClient(mstrLastModalMsg);
				}
			}
		}
		else if (strType == "modal-btnClose")
		{// 关闭消息
			WebsocketProcess::Instance()->SendCloseModal();
			mstrLastModalMsg.clear();
		}
		else if ("toolbar-btnShutdown" == strType)
		{// 关机
			{
				std::stringstream strSteam;
				strSteam << "{\"msgType\":\"" << "toolbar-btnShutdown-question" << "\"" << "}";
				mstrLastModalMsg = strSteam.str();
				WebsocketProcess::Instance()->SendToAllClient(strSteam.str());
				return;
			}
		}
		else if ("toolbar-btnShutdown-confirm" == strType)
		{// 关机确认
			//SysCtrl::SysShutdown(false);
			SysCtrl::SysShutdownCmd(false);
			qDebug() << "toolbar-btnShutdown-confirm";

			WebsocketProcess::Instance()->SendCloseModal();
		}
		else if ("toolbar-btnShutdownForce-confirm" == strType)
		{// 关机确认
		 //SysCtrl::SysShutdown(true);
			SysCtrl::SysShutdownCmd(true);
			qDebug() << "toolbar-btnShutdownForce-confirm";

			WebsocketProcess::Instance()->SendCloseModal();
		}
		else if ("toolbar-btnReboot-confirm" == strType)
		{
			SysCtrl::SysRebootCmd(false);
			qDebug() << "toolbar-btnReboot-confirm";

			WebsocketProcess::Instance()->SendCloseModal();
		}
		else if ("toolbar-btnRebootForce-confirm" == strType)
		{
			SysCtrl::SysRebootCmd(true);
			qDebug() << "toolbar-btnRebootForce-confirm";

			WebsocketProcess::Instance()->SendCloseModal();
		}
		else if ("toolbar-btnHibernates-confirm" == strType)
		{
			WebsocketProcess::Instance()->SendCloseModal();
			SysCtrl::SysSuspend(true, false);
			qDebug() << "toolbar-btnHibernates-confirm";
		}
		else if ("toolbar-btnHibernatesForce-confirm" == strType)
		{
			SysCtrl::SysSuspend(true, true);
			qDebug() << "toolbar-btnHibernates-confirm";

			WebsocketProcess::Instance()->SendCloseModal();
		}

		else if ("toolbar-btnSysLogoff" == strType)
		{
			{
				std::stringstream strSteam;
				strSteam << "{\"msgType\":\"" << "toolbar-btnSysLogoff-question" << "\"" << "}";
				mstrLastModalMsg = strSteam.str();
				WebsocketProcess::Instance()->SendToAllClient(strSteam.str());
				return;
			}
		}
		else if ("toolbar-btnSysLock-confirm" == strType)
		{
			bool bRes = SysCtrl::SysLock();
			qDebug() << "toolbar-btnSysLock:" << bRes;

			WebsocketProcess::Instance()->SendCloseModal();
		}
		else if ("toolbar-btnLogoff-confirm" == strType)
		{
			bool bRes = SysCtrl::SysLogoff(true);
			//bool bRes = SysCtrl::SysLogoffCmd(true);
			qDebug() << "toolbar-btnSysLogoff:" << bRes;

			WebsocketProcess::Instance()->SendCloseModal();
		}
		else if ("toolbar-btnScreenShot" == strType)
		{// 屏幕截图
			qDebug() << "toolbar-btnScreenShot";

			static std::thread * pThread = nullptr;
			if (nullptr == pThread)
			{
				pThread = new std::thread([&]() {				
					while (true)
					{
						QList<QScreen *> screenList = QGuiApplication::screens();
						for (size_t i = 0; i < screenList.size(); ++i)
						{
							QDateTime dtStart = QDateTime::currentDateTime();

							QScreen * pScreen = screenList.at(i);
							QPixmap bmpScreen = pScreen->grabWindow(0);
							//bmpScreen = bmpScreen.scaled(bmpScreen.width()/2, bmpScreen.height()/2, Qt::KeepAspectRatio, Qt::SmoothTransformation);

							QBuffer imgDataBuf;
							bool bRes = bmpScreen.save(&imgDataBuf, "png");
							if (!bRes)
							{
								qDebug() << "screen shot save to buffer failed！";
								return;
							}
							QByteArray byteArrayBase64 = imgDataBuf.data().toBase64(QByteArray::Base64Encoding);
							qDebug() << "png Img size:" << byteArrayBase64.size();

							QDateTime dtEnd1 = QDateTime::currentDateTime();
							qDebug() << "duration1:" << dtStart.msecsTo(dtEnd1);

							{
								Json::Value rootSend;
								rootSend["msgType"] = "sreenShotImg";
								rootSend["imgBase64"] = byteArrayBase64.toStdString();
								rootSend["imgWidth"] = bmpScreen.width();
								rootSend["imgHeight"] = bmpScreen.height();

								Json::FastWriter writer;
								std::string dataJson = writer.write(rootSend);
								WebsocketProcess::Instance()->SendToAllClient(dataJson);
							}

							QDateTime dtEnd = QDateTime::currentDateTime();
							qDebug() << "duration2:" << dtStart.msecsTo(dtEnd);

							break;
						}
						//std::this_thread::sleep_for(std::chrono::milliseconds(50));
						break;
					}
					pThread = nullptr;
				
				});
			}
		}
		else if ("keyBoardBtn" == strType)
		{// 虚拟按钮
			QString strKeys = GetJsonStrVal(root, "keys");
			if (!strKeys.isEmpty())
			{
				bool bRes = SysCtrl::Instance().KeyBoardForegroundWindow(strKeys);
				qDebug() << "KeyBoardForegroundWindow: res:" << bRes;
			}
		}
		else if ("MouseLBtnClick" == strType)
		{

			mouse_event(MOUSEEVENTF_LEFTDOWN, -1, -1, 0, 0);
			Sleep(1);
			mouse_event(MOUSEEVENTF_LEFTUP, -1, -1, 0, 0);
		}
		else if ("MouseRBtnClick" == strType)
		{

			mouse_event(MOUSEEVENTF_RIGHTDOWN, -1, -1, 0, 0);
			Sleep(1);
			mouse_event(MOUSEEVENTF_RIGHTUP, -1, -1, 0, 0);
		}
		else if ("MouseMove" == strType)
		{
			int x = GetJsonIntVal(root, "X");
			int y = GetJsonIntVal(root, "Y");

			mouse_event(MOUSEEVENTF_MOVE, x, y, 0, 0);
		}


		else {
			qDebug() << "unkown type:" << strType;
		}

	}
	catch (QString strExcpt)
	{
		qDebug() << "json exception:" << strExcpt << ".  json:" << strMsgQt;
		this->SlotStatusBarMessage(tr("Web消息解析失败2。"));
		return;
	}
	catch (...)
	{
		qDebug() << "json exception. json:" << strMsgQt;
		this->SlotStatusBarMessage(tr("Web消息解析失败3。"));
		return;
	}
}

void PCRemoteCtrl::CreateQrCode()
{
	std::vector<std::string> addrVec;
	QtuHttpWebSocket::Instance().GetLocalAddr(addrVec);
	if (addrVec.empty())
	{
		SlotStatusBarMessage(tr("获取本地IP地址失败"), StatusBarMsgColor_Red);
		//qDebug("获取本地IP地址失败");
		return;
	}
	int iPort = QtuHttpWebSocket::Instance().GetPort();
	QString strUrl = tr("http://%1:%2").arg(addrVec[0].c_str()).arg(iPort);
	// 生成二维码
	std::vector<unsigned char> bufferVec = QrEncode::Instance().ToQrEncode(strUrl.toStdString(), QrEncode::QrEncodeType_PNG, 4, 9);

	QByteArray byteArray((const char *)&bufferVec[0], bufferVec.size());
#if 0
	// 加载图像
	//QPixmap img;
	QImage img;
	if (!img.loadFromData(byteArray))
		//if (!img.load(pFileName))
	{
		SlotStatusBarMessage(tr("二维码图像加载失败"), StatusBarMsgColor_Red);
		return;
	}
	// 白色转成透明
	img = img.convertToFormat(QImage::Format_ARGB32);
	for (int iWidth = 0; iWidth < img.width(); ++iWidth)
	{
		for (int iHeight = 0; iHeight < img.height(); ++iHeight)
		{
			QColor colorTmp = img.pixelColor(iWidth, iHeight);
			if (colorTmp == QColor::fromRgb(255, 255, 255))
			{
				//qDebug() << "(" << iWidth << "," << iHeight << ")";
				img.setPixelColor(iWidth, iHeight, QColor::fromRgb(255, 255, 255, 0));
			}
		}
	}

	// 改变图像大小
	//int iSize = min(ui.labelQr->width(), ui.labelQr->height());
	//img = img.scaledToWidth(iSize);
	//img = img.scaledToHeight(iSize);
	// 显示
	//ui.labelQr->setPixmap(QPixmap::fromImage(img));

	// 设置图标 
	QPixmap imgPix = QPixmap::fromImage(img);
	QIcon iconTmp(imgPix);
	//ui.action_QrCode->setIcon(iconTmp);
#endif
	// 设置tooltip
	QByteArray byteArrayBase64 = byteArray.toBase64(QByteArray::Base64Encoding);
	//QString strToolTip = tr("<html><head/><body><p><img src=\"data:image/bmp;base64,%1\"/></p></body></html>").arg(byteArrayBase64.toStdString().c_str());
	QString strAddr = tr("<html><head/><body><p>同一局域网的手机扫描二维码<br/>或者浏览器地址栏输入：<a href=\"%1\">%1</a></p></body></html>").arg(strUrl);
	QString strQrCode = QString("<html><head/><body><p><img style=\"width:100%;height:100%;\" src=\"data:image/bmp;base64,%1\"/></p></body></html>").arg(byteArrayBase64.toStdString().c_str());


	ui.labelAddr->setText(strAddr);
	ui.labelQrcode->setText(strQrCode);
}

void PCRemoteCtrl::ReadJsonConfig()
{
	QString dirPath = QCoreApplication::applicationDirPath();
	dirPath += "\\config.json";
	if (!QFile::exists(dirPath))
	{
	}
		FILE * fl = nullptr;
		_wfopen_s(&fl, dirPath.toStdWString().c_str(), L"rb+");
		if (!fl)
		{
			return;
		}
		fseek(fl, 0, SEEK_END);
		long lFileLen = ftell(fl);
		std::string strFileContent;
		strFileContent.resize(lFileLen);
		fseek(fl, 0, SEEK_SET);
		size_t fReadLen = fread((void*)strFileContent.data(), 1, lFileLen, fl);
		if (fReadLen != lFileLen)
		{
			fclose(fl);
			fl = nullptr;
			qDebug() << "读取配置文件config.json错误";
			this->SlotStatusBarMessage(tr("读取配置文件config.json错误"));
			return;
		}
		fclose(fl);
		fl = nullptr;

		if (strFileContent.size() < 5)
		{
			return;
		}
		if (-17 == strFileContent[0]
			&& -69 == strFileContent[1]
			&& -65 == strFileContent[2]
			)
		{// 去掉UTF8文件头
			strFileContent = strFileContent.substr(3);
		}

		try
		{
			Json::Reader reader;
			Json::Value root;
			if (!reader.parse(strFileContent, root))
			{// json解析
				qDebug() << "config json parse failed. json:";
				this->SlotStatusBarMessage(tr("json配置文件解析失败1。"));
				return;
			}

			miWebPort = GetJsonIntVal(root, "wwwPort");
		}
		catch (QString strExcpt)
		{
			qDebug() << "json exception" << strExcpt;
			this->SlotStatusBarMessage(tr("json配置解析失败2。"));
			return;
		}
		catch (...)
		{
			qDebug() << "json exception. json";
			this->SlotStatusBarMessage(tr("json配置解析失败3。"));
			return;
		}
}

void PCRemoteCtrl::InitWebsocket()
{
	QString strWeb;
	QString dirPath = QCoreApplication::applicationDirPath();
	strWeb = dirPath + "\\" + tr("wwwRoot");
	//#ifdef _DEBUG
	//	strWeb = tr("./wwwRoot");
	//#endif
	mstrWebRoot = strWeb;
	connect(&QtuHttpWebSocket::Instance(), SIGNAL(SignalWebMsg(QString)), this, SLOT(SlotWebMsg(QString)));
	if (!QtuHttpWebSocket::Instance().Init(miWebPort, strWeb.toLocal8Bit().toStdString()/*"./wwwRoot"*/, "websocket.html"))
	{
		//QMessageBox::information(this, tr("提示"), tr("HTTP服务初始化失败"));
		SlotStatusBarMessage(tr("HTTP服务初始化失败"), StatusBarMsgColor_Red);
	}
	else
	{
		CreateQrCode();
		InitUdpBroadcast();
	}
}

void PCRemoteCtrl::InitUdpBroadcast()
{
	std::vector<std::string> addrVec;
	QtuHttpWebSocket::Instance().GetLocalAddr(addrVec);
	if (addrVec.empty())
	{
		SlotStatusBarMessage(tr("获取本地IP地址失败"), StatusBarMsgColor_Red);
		//qDebug("获取本地IP地址失败");
		return;
	}
	int iPort = QtuHttpWebSocket::Instance().GetPort();
	QString strUrl = tr("http://%1:%2").arg(addrVec[0].c_str()).arg(iPort);
	QVector<quint16> portVec;
	for (quint16 i = 30000; i < 30000 + 10; ++i)
	{
		portVec << i;
	}
	if (!udpBroadcast.InitServer(QHostInfo::localHostName(), strUrl, portVec))
	{
		SlotStatusBarMessage(tr("设置UDP接收监听失败"), StatusBarMsgColor_Red);
		return;
	}
}

void PCRemoteCtrl::InitTray()
{
	minimizeAction = new QAction(tr("隐藏"), this);
	connect(minimizeAction, &QAction::triggered, this, &QWidget::hide);

	//maximizeAction = new QAction(tr("Ma&ximize"), this);
	//connect(maximizeAction, &QAction::triggered, this, &QWidget::showMaximized);

	restoreAction = new QAction(tr("显示"), this);
	connect(restoreAction, &QAction::triggered, this, &QWidget::showNormal);

	quitAction = new QAction(tr("退出"), this);
	connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);

	trayIconMenu = new QMenu(this);
	trayIconMenu->addAction(minimizeAction);
	//trayIconMenu->addAction(maximizeAction);
	trayIconMenu->addAction(restoreAction);
	trayIconMenu->addSeparator();
	trayIconMenu->addAction(quitAction);

	trayIcon = new QSystemTrayIcon(this);
	trayIcon->setContextMenu(trayIconMenu);
	this->trayIcon->setIcon(windowIcon());
	this->trayIcon->show();	// 不设置显示将不能触发信号QSystemTrayIcon::activated，不知什么bug，不调用这个其实也可以在托盘显示的

	connect(this->trayIcon, &QSystemTrayIcon::activated, [&](QSystemTrayIcon::ActivationReason reason) {
		if (reason == QSystemTrayIcon::DoubleClick)
		{
			if (this->isVisible())
			{
				this->hide();
			}
			else
			{
				this->showNormal();
			}
		}
	});
}

void PCRemoteCtrl::changeEvent(QEvent * event)
{
	if (event->type() == QEvent::WindowStateChange)
	{
		//qDebug() << "QEvent::WindowStateChange";
		QWindowStateChangeEvent * pEvent = static_cast<QWindowStateChangeEvent*>(event);
		//qDebug() << pEvent->oldState() << this->windowState();
		if (this->windowState() == Qt::WindowMinimized)
		{
			//qDebug() << "Qt::WindowMinimized";
			this->hide();	// 最小化时隐藏
		}
	}
}


void PCRemoteCtrl::closeEvent(QCloseEvent *event)
{
	// 销毁
	QtuHttpWebSocket::Instance().UnInit();
}

////////////////////////////////////////////////////////////////
QString PCRemoteCtrl::GetJsonStrVal(Json::Value& node, const std::string strNodeName)
{
	if (IsJsonMemberValid(node, strNodeName))
	{
		if (!node[strNodeName.c_str()].isString())
		{
			QString strErr = tr("json字段(%1)的值不是字符串").arg(QString::fromStdString(strNodeName));
			throw strErr;
			return "";
		}
		return QString::fromUtf8(node[strNodeName.c_str()].asString().c_str());
	}
	return "";
}
std::string PCRemoteCtrl::GetJsonStdStrVal(Json::Value& node, const std::string strNodeName)
{
	if (IsJsonMemberValid(node, strNodeName))
	{
		if (!node[strNodeName.c_str()].isString())
		{
			QString strErr = tr("json字段(%1)的值不是字符串").arg(QString::fromStdString(strNodeName));
			throw strErr;
			return "";
		}
		return node[strNodeName.c_str()].asString();
	}
	return "";
}

int PCRemoteCtrl::GetJsonIntVal(Json::Value& node, const std::string strNodeName)
{
	if (IsJsonMemberValid(node, strNodeName))
	{
		if (!node[strNodeName.c_str()].isInt())
		{
			QString strErr = tr("json字段(%1)的值不是整数").arg(QString::fromStdString(strNodeName));
			throw strErr;
			return 0;
		}
		return node[strNodeName.c_str()].asInt();
	}
	return 0;
}
__int64 PCRemoteCtrl::GetJsonInt64Val(Json::Value& node, const std::string strNodeName)
{
	if (IsJsonMemberValid(node, strNodeName))
	{
		if (!node[strNodeName.c_str()].isNumeric())
		{
			QString strErr = tr("json字段(%1)的值不是64位整数").arg(QString::fromStdString(strNodeName));
			throw strErr;
			return 0;
		}
		return node[strNodeName.c_str()].asInt64();
	}
	return 0;
}

double PCRemoteCtrl::GetJsonDoubleVal(Json::Value& node, const std::string strNodeName)
{
	if (IsJsonMemberValid(node, strNodeName))
	{
		if (!node[strNodeName.c_str()].isDouble())
		{
			QString strErr = tr("json字段(%1)的值不是double类型").arg(QString::fromStdString(strNodeName));
			throw strErr;
			return 0;
		}
		return node[strNodeName.c_str()].asDouble();
	}
	return 0;
}

bool PCRemoteCtrl::GetJsonboolVal(Json::Value& node, const std::string strNodeName)
{
	if (IsJsonMemberValid(node, strNodeName))
	{
		if (!node[strNodeName.c_str()].isBool())
		{
			QString strErr = tr("json字段(%1)的值不是布尔型").arg(QString::fromStdString(strNodeName));
			throw strErr;
			return false;
		}
		return node[strNodeName.c_str()].asBool();
	}
	return false;
}


bool PCRemoteCtrl::IsJsonMemberValid(Json::Value& node, const std::string strNodeName)
{
	if (!node.isMember(strNodeName))
	{
		QString strErr = tr("json中%1字段不存在").arg(QString::fromStdString(strNodeName));
		throw strErr;
		return false;
	}
	if (!node[strNodeName.c_str()].isNull())
	{
		return true;
	}
	else
	{
		QString strErr = tr("json中%1字段不存在").arg(QString::fromStdString(strNodeName));
		throw strErr;
		return false;
	}
}

////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
WebsocketProcess::WebsocketProcess()
{

}

WebsocketProcess::~WebsocketProcess()
{

}

QSharedPointer<WebsocketProcess> WebsocketProcess::Instance()
{
	static QSharedPointer<WebsocketProcess> instance(new WebsocketProcess());
	return instance;
}

void WebsocketProcess::SendToAllClient(std::string strData)
{
	QtuHttpWebSocket::Instance().SendToAllClient(strData);
}



void WebsocketProcess::SendStatusBarMsg(QString strMessage, StatusBarMsgColor iLevel, int iTimeoutMs)
{
	std::stringstream strSteam;
	strSteam << "{\"msgType\":\"statusBarMsg\""
		<< ", \"msg\":\"" << strMessage.toUtf8().toStdString() << "\""
		<< ", \"color\":" << iLevel
		<< ", \"timeoutMs\":" << iTimeoutMs
		<< "}";
	SendToAllClient(strSteam.str());
}


void WebsocketProcess::SendCloseModal()
{
	// 关闭
	std::stringstream strSteam;
	strSteam << "{\"msgType\":\"" << "modal-btnClose" << "\"" << "}";
	WebsocketProcess::Instance()->SendToAllClient(strSteam.str());
}


